/**
  ******************************************************************************
  * @file    Project/STM8L15x_StdPeriph_Template/main.c
  * @author  MCD Application Team
  * @version V1.6.1
  * @date    30-September-2014
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2014 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm8l15x.h"
#include "contiki.h"
#include <stdbool.h>
#include <stdio.h>

/** @addtogroup STM8L15x_StdPeriph_Template
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
typedef enum key_event_e
{
    KEY_EVENT_NONE,
    KEY_EVENT_SHORT_UP,
    KEY_EVENT_LONG_UP,
    KEY_EVENT_HOLD_UP,
    KEY_EVENT_VLONG_UP,
    KEY_EVENT_CLICK,
    KEY_EVENT_SHORT,
    KEY_EVENT_HOLD,
    KEY_EVENT_LONG,
    KEY_EVENT_VLONG,
    KEY_EVENT_DCLICK
} key_event_t;

typedef enum key_msg_e
{
    KEY_MSG_NONE,
    KEY_MSG_SHORT_UP,
    KEY_MSG_LONG_UP,
    KEY_MSG_HOLD_UP,
    KEY_MSG_VLONG_UP,
    KEY_MSG_CLICK,
    KEY_MSG_SHORT,
    KEY_MSG_HOLD,
    KEY_MSG_LONG,
    KEY_MSG_VLONG,
    KEY_MSG_DCLICK
} key_msg_t;

typedef struct
{
    uint8_t key;
    key_event_t event;
    key_msg_t msg;
} key_map_t;

typedef struct
{
    uint8_t key;
    key_msg_t msg;
} key_report_t;

typedef struct div_result
{
    uint16_t Quotient;
    uint16_t Remainder;
} div_result_t;

/* Private define ------------------------------------------------------------*/
#ifdef __DBG_LOG__
#define LOG_INF printf
#endif

#define ARRAY_SZ(a)     (sizeof(a) / sizeof(a[0]))

/************PORT A*****************/
#define UART_TX_O_PIN                  GPIO_Pin_2
#define UART_RX_I_PIN                  GPIO_Pin_3

/************PORT B*****************/

/************PORT C*****************/
#define KEY_I_PIN                      GPIO_Pin_1
#define LED_R_O_PIN                    GPIO_Pin_7

/************PORT D*****************/

/************PORT E*****************/
#define LED_G_O_PIN                    GPIO_Pin_7

/************PORT F*****************/
#define UART_RTS_O_PIN                 GPIO_Pin_0

#define WINDOW_VALUE       0x5f
#define COUNTER_INIT       0x7f

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static @tiny volatile clock_time_t current_clock = 0;
static @tiny volatile unsigned long current_seconds = 0;
static @tiny unsigned int second_countdown = CLOCK_SECOND;

static @tiny process_event_t event_key = 0;

static const key_map_t key_map[] =
{
//    {1, KEY_EVENT_CLICK, KEY_MSG_CLICK},
//    {1, KEY_EVENT_LONG, KEY_MSG_LONG},
//    {1, KEY_EVENT_DCLICK, KEY_MSG_DCLICK},

    {1, KEY_EVENT_SHORT, KEY_MSG_SHORT},
    {1, KEY_EVENT_HOLD, KEY_MSG_HOLD},
};

static @tiny uint8_t uart_rx_buf[16];
static @tiny uint8_t uart_rx_cnt = 0;
static @tiny uint8_t uart_rx_timeout = 0;

/* Private function prototypes -----------------------------------------------*/
void GPIO_Config(void);
void IWDG_Config(void);
void WWDG_Config(void);
void Refresh_WWDG_Window(void);
void UART_Config(void);
void TIM2_Config(void);
void TIM4_Config(void);
static void Delay_us(uint16_t us);
void uart_send_string(const char *str);
void uart_recv_callback(void);

void SysTick_callback(void);

#ifdef _RAISONANCE_
#define PUTCHAR_PROTOTYPE int putchar (char c)
#define GETCHAR_PROTOTYPE int getchar (void)
#elif defined (_COSMIC_)
#define PUTCHAR_PROTOTYPE char putchar (char c)
#define GETCHAR_PROTOTYPE char getchar (void)
#else /* _IAR_ */
#define PUTCHAR_PROTOTYPE int putchar (int c)
#define GETCHAR_PROTOTYPE int getchar (void)
#endif /* _RAISONANCE_ */

void div_8(uint8_t Dividend, uint8_t Divisor, div_result_t* divResult);
void div_16(uint16_t Dividend, uint16_t Divisor, div_result_t* divResult);
uint16_t mult_8(uint8_t a, uint8_t b);
uint32_t mult_16(uint16_t a, uint16_t b);

PROCESS(key_process, "Key");
PROCESS(led_process, "Led");
PROCESS(blink_process, "Blink");
PROCESS(uart_rec_process, "Uart");

/* Private functions ---------------------------------------------------------*/
void SysTick_callback(void)
{
    current_clock++;

    if(etimer_pending() && etimer_next_expiration_time() <= current_clock)
    {
        etimer_request_poll();
    }

    if(--second_countdown == 0)
    {
        current_seconds++;
        second_countdown = CLOCK_SECOND;
    }
}

clock_time_t clock_time(void)
{
    return current_clock;
}

unsigned long clock_seconds(void)
{
    return current_seconds;
}

void GPIO_Config(void)
{
    GPIO_Init(GPIOA, UART_RX_I_PIN, GPIO_Mode_In_FL_No_IT);
    GPIO_Init(GPIOE, LED_G_O_PIN, GPIO_Mode_Out_PP_Low_Slow);
    GPIO_Init(GPIOC, KEY_I_PIN, GPIO_Mode_In_PU_No_IT);
    GPIO_Init(GPIOC, LED_R_O_PIN, GPIO_Mode_Out_PP_Low_Slow);
    GPIO_Init(GPIOF, UART_RTS_O_PIN, GPIO_Mode_Out_PP_High_Slow);
}

void IWDG_Config(void)
{
    /* Enable IWDG (the LSI oscillator will be enabled by hardware) */
    IWDG_Enable();

    /* IWDG timeout equal to 128 ms (the timeout may varies due to LSI frequency
       dispersion) */
    /* Enable write access to IWDG_PR and IWDG_RLR registers */
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);

    /* IWDG counter clock: LSI/64 */
    IWDG_SetPrescaler(IWDG_Prescaler_64);

    /* Set counter reload value to obtain 250ms IWDG Timeout.
      Counter Reload Value = 250ms/IWDG counter clock period
                           = 250ms / (LSI/128)
                           = 0.25s / (LsiFreq/128)
                           = LsiFreq/(128 * 4)
                           = LsiFreq/512
     */
    IWDG_SetReload((uint8_t)128);

    /* Reload IWDG counter */
    IWDG_ReloadCounter();
}

void WWDG_Config(void)
{
    /* WWDG Configuration */
    /* Watchdog Window= 0x7F step to 0x3F step
    = (0x7F-0x3F) * 1 step
    = 64 * 1 step
    = 64 * (12288/2Mhz)
    = 393.216ms
    */
    /* Allowed Window = (0x7F-window) * 1 step
    = (0x7F-0x77) * 1 step
    = 7 * 1 step
    = 7 * (12288/2Mhz)
    = 43.008ms
    */
    /* So the non allowed window starts from 0.0ms to 43.008ms
    and the alowed window starts from 43.008ms to 393.216ms */
    WWDG_Init(COUNTER_INIT, WINDOW_VALUE);
}

void Refresh_WWDG_Window(void)//喂狗
{
    if((WWDG_GetCounter() & 0x7F) < WINDOW_VALUE)
    {
        WWDG_SetCounter(COUNTER_INIT);
    }
}

void UART_Config(void)
{
    CLK_PeripheralClockConfig(CLK_Peripheral_USART1, ENABLE);

    /* Set the USART TX and USART RX at high level */
    GPIO_ExternalPullUpConfig(GPIOA, UART_TX_O_PIN, ENABLE);
    GPIO_ExternalPullUpConfig(GPIOA, UART_RX_I_PIN, ENABLE);
    //01: USART1_TX on PA2 and USART1_RX on PA3
    SYSCFG_REMAPPinConfig(REMAP_Pin_USART1TxRxPortA, ENABLE);

    /* EVAL COM (UART) configuration -----------------------------------------*/
    /* USART configured as follow:
          - BaudRate = 38400 baud
          - Word Length = 8 Bits
          - One Stop Bit
          - No parity
          - Receive and transmit enabled
          - UART Clock disabled
    */
    USART_Init(USART1, (uint32_t)38400, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No,
               USART_Mode_Rx | USART_Mode_Tx);

    /* Enable the UART Receive interrupt: this interrupt is generated when the UART
      receive data register is not empty */
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    /* Enable UART */
    USART_Cmd(USART1, ENABLE);

}

void TIM2_Config(void)
{
    CLK_PeripheralClockConfig(CLK_Peripheral_TIM2, ENABLE);
    TIM2_TimeBaseInit(TIM2_Prescaler_2, TIM2_CounterMode_Up, 65535);
    TIM2_ARRPreloadConfig(ENABLE);
}

void TIM4_Config(void)
{
    CLK_PeripheralClockConfig(CLK_Peripheral_TIM4, ENABLE);
    TIM4_TimeBaseInit(TIM4_Prescaler_8, 249);
    TIM4_ClearFlag(TIM4_FLAG_Update);
    TIM4_ITConfig(TIM4_IT_Update, ENABLE);
    TIM4_Cmd(ENABLE);
}

static void Delay_us(uint16_t us)
{
    TIM2_SetCounter(0xffff - us);
    TIM2_ClearFlag(TIM2_FLAG_Update);
    TIM2_Cmd(ENABLE);

    while(TIM2_GetFlagStatus(TIM2_FLAG_Update) == RESET)
    {

    }

    TIM2_Cmd(DISABLE);
}

void div_8(uint8_t Dividend, uint8_t Divisor, div_result_t* divResult)
{
    uint8_t i = 0;
    uint16_t a = Dividend;
    uint16_t b = Divisor;

    divResult->Quotient = 0;
    divResult->Remainder = 0;

    if(Divisor == 0)
    {
        return;
    }

    while(b <= a)
    {
        b <<= 1;
        i++;
    }

    while(i--)
    {
        divResult->Quotient <<= 1;
        b >>= 1;

        if(a >= b)
        {
            a -= b;
            divResult->Quotient |= 0x01;
        }
    }

    divResult->Remainder = (uint8_t)a;
}

void div_16(uint16_t Dividend, uint16_t Divisor, div_result_t* divResult)
{
    uint8_t i = 0;
    uint32_t a = Dividend;
    uint32_t b = Divisor;

    divResult->Quotient = 0;
    divResult->Remainder = 0;

    if(Divisor == 0)
    {
        return;
    }

    while(b <= a)
    {
        b <<= 1;
        i++;
    }

    while(i--)
    {
        divResult->Quotient <<= 1;
        b >>= 1;

        if(a >= b)
        {
            a -= b;
            divResult->Quotient |= 0x01;
        }
    }

    divResult->Remainder = (uint16_t)a;
}

uint16_t mult_8(uint8_t a, uint8_t b)
{
    uint16_t aa = a;
    uint16_t result = 0;

    if(b > a)
    {
        aa = b;
        b = a;
    }

    while(b)
    {
        if(b & 0x01)
        {
            result += aa;
        }

        aa <<= 1;
        b >>= 1;
    }

    return result;
}

uint32_t mult_16(uint16_t a, uint16_t b)
{
    uint32_t aa = a;
    uint32_t result = 0;

    if(b > a)
    {
        aa = b;
        b = a;
    }

    while(b)
    {
        if(b & 0x01)
        {
            result += aa;
        }

        aa <<= 1;
        b >>= 1;
    }

    return result;
}

void uart_send_num(uint8_t num)
{
    uint8_t c[3];
    uint8_t i;
    div_result_t div_result;

    div_8(num, 100, &div_result);
    c[0] = (uint8_t)div_result.Quotient;
    num = (uint8_t)div_result.Remainder;

    div_8(num, 10, &div_result);
    c[1] = (uint8_t)div_result.Quotient;
    c[2] = (uint8_t)div_result.Remainder;

    if(c[0] == 0)
    {
        c[0] = ' ';
    }
    else
    {
        c[0] += '0';
    }

    if((c[1] == 0) && (c[0] == ' '))
    {
        c[1] = ' ';
    }
    else
    {
        c[1] += '0';
    }

    c[2] += '0';

    for(i = 0; i < 3; i++)
    {
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

        USART_SendData8(USART1, c[i]);
    }
}

void uart_send_string(const char *str)
{
    while(*str != '\0')
    {
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

        USART_SendData8(USART1, *str++);
    }
}

PROCESS_THREAD(blink_process, ev, data)
{
    static struct etimer et_blink;

    PROCESS_BEGIN();

    etimer_set(&et_blink, CLOCK_SECOND / 2);

    while(1)
    {
        PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et_blink));
        etimer_reset(&et_blink);
        GPIO_ToggleBits(GPIOE, LED_G_O_PIN);
    }

    PROCESS_END();
}

PROCESS_THREAD(key_process, ev, data)
{
    static struct etimer et_key;
    @tiny static key_event_t key_event = KEY_EVENT_NONE;
    @tiny static uint8_t key_value = 0;
    @tiny static uint8_t key_filter = 0;
    @tiny static uint8_t key_last = 0;
    @tiny static uint16_t key_life = 0;
    @tiny static uint8_t key_release = 0;
    @tiny static uint8_t key_click_cnt = 0;
    @tiny static bool key_notify = false;
    @tiny static key_report_t key_report;
    uint8_t key;
    uint8_t i;

    PROCESS_BEGIN();

    event_key = process_alloc_event();
    etimer_set(&et_key, CLOCK_SECOND / 100);

    while(1)
    {
        PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et_key));
        etimer_reset(&et_key);

        key = (GPIO_ReadInputDataBit(GPIOC, KEY_I_PIN) == RESET) ? 1 : 0;

        if(key != key_filter)
        {
            key_filter = key;
        }
        else
        {
            if(key != key_last)
            {
                if(key == 0)
                {
                    if(key_life >= 100)
                    {
                        key_event = KEY_EVENT_LONG_UP;
                        key_notify = true;
                        key_click_cnt = 0;
                    }
                    else
                    {
                        key_release = 30;
                    }
                }
                else
                {
                    key_life = 0;

                    if(key == key_value)
                    {
                        key_click_cnt++;
                    }
                    else
                    {
                        key_click_cnt = 1;
                        key_value = key;
                    }
                }
            }
            else
            {
                if(key == 0)
                {
                    if(key_click_cnt)
                    {
                        if(key_release == 0)
                        {
                            if(key_click_cnt >= 2)
                            {
                                key_event = KEY_EVENT_DCLICK;
                            }
                            else
                            {
                                key_event = KEY_EVENT_CLICK;
                            }

                            key_notify = true;
                            key_click_cnt = 0;
                        }
                        else
                        {
                            key_release--;
                        }
                    }
                    else
                    {
                        key_value = 0;
                    }
                }
                else
                {
                    if(key_life == 0)
                    {
                        key_event = KEY_EVENT_SHORT;
                        key_notify = true;
                    }
                    else if(key_life == 100)
                    {
                        key_event = KEY_EVENT_LONG;
                        key_notify = true;
                    }
                    else if(key_life >= 120)
                    {
                        key_life = 100;
                        key_event = KEY_EVENT_HOLD;
                        key_notify = true;
                    }

                    key_life++;
                }
            }

            key_last = key;
        }

        if(key_notify)
        {
            key_notify = false;

            for(i = 0; i < ARRAY_SZ(key_map); i++)
            {
                if((key_map[i].key == key_value) && (key_map[i].event == key_event))
                {
                    key_report.key = key_map[i].key;
                    key_report.msg = key_map[i].msg;
                    process_post(&led_process, event_key, &key_report);
                }
            }
        }

    }

    PROCESS_END();
}

PROCESS_THREAD(led_process, ev, data)
{
    key_report_t *key_report_rev;

    PROCESS_BEGIN();

    while(1)
    {
        PROCESS_WAIT_EVENT_UNTIL(ev == event_key);
        key_report_rev = (key_report_t *)data;

        switch(key_report_rev->msg)
        {
            case KEY_MSG_HOLD:
            case KEY_MSG_SHORT:
            {
                if(key_report_rev->key == 1)
                {
                    GPIO_ToggleBits(GPIOC, LED_R_O_PIN);
                }
            }
            break;

            case KEY_MSG_CLICK:
            {
                if(key_report_rev->key == 1)
                {
                    GPIO_ToggleBits(GPIOC, LED_R_O_PIN);
                }
            }
            break;

            case KEY_MSG_DCLICK:
            {
                if(key_report_rev->key == 1)
                {
                    GPIO_ToggleBits(GPIOC, LED_R_O_PIN);
                }
            }
            break;

            case KEY_MSG_LONG:
            {
                if(key_report_rev->key == 1)
                {
                    GPIO_ToggleBits(GPIOC, LED_R_O_PIN);
                }
            }
            break;

            default:
                break;
        }
    }

    PROCESS_END();
}

void uart_recv_callback(void)
{
    uart_rx_cnt &= 0x0f;
    uart_rx_buf[uart_rx_cnt] = USART_ReceiveData8(USART1);
    uart_rx_cnt++;
    uart_rx_timeout = 1;
}

PROCESS_THREAD(uart_rec_process, ev, data)
{
    static struct etimer et_uart;
    uint8_t cnt;

    PROCESS_BEGIN();

    etimer_set(&et_uart, CLOCK_SECOND / 200);
    UART_Config();

    uart_send_string("start...\r\n");

    while(1)
    {
        PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et_uart));
        etimer_reset(&et_uart);

        if(uart_rx_timeout == 0)
        {
            if(uart_rx_cnt != 0)
            {
                cnt = uart_rx_cnt;
                uart_rx_cnt = 0;
#ifdef __DBG_LOG__
                uart_send_string("uart rx count = ");
                uart_send_num(cnt);
                uart_send_string("\r\n");
#endif
            }
        }
        else
        {
            uart_rx_timeout--;
        }
    }

    PROCESS_END();
}

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
void main(void)
{
    if(RST_GetFlagStatus(RST_FLAG_WWDGF) != RESET)
    {
        RST_ClearFlag(RST_FLAG_WWDGF);
    }

    WWDG_Config();
    GPIO_Config();
    TIM2_Config();
    TIM4_Config();

    enableInterrupts();

    process_init();
    process_start(&etimer_process, NULL);
    process_start(&uart_rec_process, NULL);
    process_start(&blink_process, NULL);
    process_start(&led_process, NULL);
    process_start(&key_process, NULL);

    /* Infinite loop */
    while(1)
    {
        do
        {
            Refresh_WWDG_Window();
        }
        while(process_run() > 0);

        /* Idle! */
        /* Stop processor clock */
        wfi();
    }
}

#ifdef __DBG_LOG__
PUTCHAR_PROTOTYPE
{
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

    USART_SendData8(USART1, c);

    return (c);
}

#endif

#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

    /* Infinite loop */
    while(1)
    {
    }
}
#endif

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
